package ngram_converter;

import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.array.TLongArrayList;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.List;
import ngram.exception.NoNGramFilesFoundException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import main.NGramWorker;
import main.errorUpdate.ErrorUpdate;
import main.textUpdate.StatusUpdate;
import ngram.util.CountingDataInputStream;
import ngram.util.IOUtil;
import org.apache.commons.io.FilenameUtils;
import processorIO.FileIO;
import processorIO.FilePath;
import swingExtended.progressPanel.ProgressPanel;

/**
 *
 * @author g1wshimu
 */
public abstract class Converter extends NGramWorker {

    public enum FileExistsOption {

        SKIP("Skip conversion for that file"),
        OVERWRITE("Overwrite existing files");

        private final String DESCRIPTION;

        private FileExistsOption(String description) {
            this.DESCRIPTION = description;
        }

        @Override
        public String toString() {
            return this.DESCRIPTION;
        }
    }

    protected final File inputDirectory, outputDirectory;

    protected final FileExistsOption FILE_EXISTS_OPTION;

    private final String EXTENSION;

        /** 
     * The number of year entries to include before skipping to the
     * next n-gram.
     */
    public static final int CAPACITY = 1000;

    protected final TIntArrayList YEARS = new TIntArrayList(CAPACITY);

    protected final TLongArrayList MATCH_COUNTS = new TLongArrayList(CAPACITY);

    protected final TLongArrayList VOLUME_COUNTS = new TLongArrayList(CAPACITY);
    
    public Converter(ProgressPanel panel, File inputDirectory, File outputDirectory,
            FileExistsOption option, String ext) {
        super(panel);
        this.inputDirectory = inputDirectory;
        this.outputDirectory = outputDirectory;
        this.FILE_EXISTS_OPTION = option;
        this.EXTENSION = ext;
    }

    protected String getNewFileName(File inFile, File outDir,
            String subFolder, String ext) {
        return new FilePath(FilePath.join(outDir.getAbsolutePath(), subFolder), FilenameUtils.getBaseName(inFile.getName()), ext).getFullPath();
    }
    
    private final Pattern N_GRAM_PATTERN = Pattern.compile("[^\\d](\\d)gram");

    /**
     * Returns the number of grams by looking at the file name. Returns 0
     * if the file name does not match the pattern.
     * @param fileName
     * @return 
     */
    protected int getGram(String fileName) {
        Matcher m = N_GRAM_PATTERN.matcher(fileName);
        if (m.find()) {
            return Integer.parseInt(m.group(1));
        } else {
            return 0;
        }
    }

    protected abstract void convert(File input, 
            File output) throws Exception;
    
    @Override
    public final void process(String inFile) throws Exception {

        final File inputFile = new File(inFile);

        final String outFileName = getNewFileName(inputFile,
                outputDirectory, "", EXTENSION);
        final File outputFile = new File(outFileName);
        if ((FILE_EXISTS_OPTION == FileExistsOption.SKIP)
                && outputFile.exists()) {
            // Skip this file
            publish(new StatusUpdate(PANEL,
                    String.format("Skipped conversion for %s", outFileName)));
            return;
        }

        FileIO.createFile(outFileName);

        boolean failed = false;
        try {
            convert(inputFile, outputFile);
        } catch (Exception ex) {
            publish(new ErrorUpdate(PANEL, "Unexpected error while converting", ex));
            failed = true;
        }
        
        /*
        try(BufferedReader in = IOUtil.getBufferedReader(inFile);
                CountingDataInputStream out = IOUtil.getCountableInputStream(outFileName);) {
            
        } */
        
        
        if (isCancelled() || failed) {
            publish(new StatusUpdate(PANEL, "Did not finish converting ", outFileName));

            if (new File(outFileName).delete()) {
                publish(new StatusUpdate(PANEL, "Successfully deleted the file"));
            } else {
                publish(new ErrorUpdate(PANEL, "Failed to delete the file: " + outFileName));
            }
        }
    }

    @Override
    protected String getHeader() {
        return "";
    }

    @Override
    protected List<String> initFiles() throws IOException,
            NoNGramFilesFoundException {
        return IOUtil.getFiles(inputDirectory);
    }
}
